/********************************************************/
/*	Copyright (C) 2016 Gong Li Bin		 	*/
/*	Project:	GlbRtbEng-1.0.0			*/
/*	Author:		gong_libin			*/
/*	Date:		2016_06_26			*/
/*	File:		GlbRtbMain.cpp			*/
/********************************************************/

#include "GlbRtbMain.h"

CGlbRtbAdvs g_CAdvs;
CGlbRtbArea g_CArea;
GLBRTBCONF_S g_stConf;
GlbNet::CGlbUdp g_CUdp;
GlbCls::CGlbRwLock g_CRwLock;

extern GLBRTBINDEX_C g_CBrw;
extern GLBRTBINDEX_C g_COsn;
extern GLBRTBINDEX_C g_CLoc;
extern GLBRTBINDEX_C g_CMdi;
extern GLBRTBINDEX_C g_CSrc;
extern GLBRTBINDEX_C g_CTyp;

int main(int argc, char* argv[])
{
	int iCount = 0;
 	int iIndex = 0;
	int iReturn = 0;
	int iThread = 1;
	bool bInit = false;
	char szPort[GLB_BYTE16] = "20162";
	GlbCls::CGlbThread* pCThread = NULL;
	char* pszConf = (char*)"../conf/GlbRtbEng.conf";

	struct option stOpt[] = {
		{"init", 0, 0, 'i'},
		{"conf", 1, 0, 'c'},
		{"port", 1, 0, 'p'},
		{"thread", 1, 0, 't'},
		{"version", 0, 0, 'v'},
		{"help", 0, 0, 'h'},
		{0, 0, 0, 0},
	};

	while (GLB_FAILURE != (iReturn = getopt_long(argc, argv, "ic:p:t:vh", stOpt, &iIndex))) {
		switch (iReturn) {
			case 'i':
				bInit = true;
				break;
			case 'c':
				pszConf = optarg;
				break;
			case 'p':
				strcpy(szPort, optarg);
				break;
			case 't':
				iThread = atoi(optarg);
				break;
			case 'v':
				GLB_PRINT("Copyright (C) 2016 Gong Li Bin	\n");
				GLB_PRINT("GlbRtbEng-1.0.0 Build on %s %s.\n", __DATE__, __TIME__);
				return GLB_SUCCESS;
			case 'h':
				GLB_PRINT("Usage: %s [options]\n", argv[0]);
				GLB_PRINT("Valid options are:\n");
				GLB_PRINT("[0] -i or --init:       initialize\n");
				GLB_PRINT("[0] -c or --conf:       set configure\n");
				GLB_PRINT("[0] -p or --port:       set listen port\n");
				GLB_PRINT("[0] -t or --thread:     set thread number\n");
				GLB_PRINT("[0] -v or --version:    show version.\n");
				GLB_PRINT("[0] -h or --help:       show help.\n");
				GLB_PRINT("Copyright (C) 2016 Gong Li Bin	\n");
				GLB_PRINT("GlbRtbEng-1.0.0 Build on %s %s.\n", __DATE__, __TIME__);
				return GLB_SUCCESS;
			default:
				return GLB_SUCCESS;
		}
	}

	GlbRtbUtilSig();

	/* conf */
	GLB_PRINT("Will GlbRtbUtilConfLoad ... ");
	if (GLB_SUCCESS == GlbRtbUtilConfLoad(pszConf)) {
		GLB_PRINT("success\n");
		#ifdef _GLB_DEBUG_
			GlbRtbUtilConfShow();
		#endif
	}
	else {
		GLB_PRINT("failure\n");
		return GLB_FAILURE;
	}

	/* init */
	if (true == bInit) {
		GlbRtbUtilKernel();
		GLB_PRINT("Will GlbRtbUtilCreateAll ...\n");
		if (GLB_SUCCESS != GlbRtbUtilCreateAll()) {
			GLB_PRINT("Failed to GlbRtbUtilCreateAll\n");
			return GLB_FAILURE;
		}
	}
	else {
		GLB_PRINT("Will GlbRtbUtilAttachAll ...\n");
		if (GLB_SUCCESS != GlbRtbUtilAttachAll()) {
			GLB_PRINT("Failed to GlbRtbUtilAttachAll\n");
			return GLB_FAILURE;
		}
	}

	/* fork */
	#ifdef _GLB_DEBUG_
		iThread = 1;
	#else
		GlbFork();
	#endif

	/* lock */
	GLB_PRINT("Will GlbRtbUtilRwLockNew ...\n");
	if (GLB_SUCCESS == GlbRtbUtilRwLockNew()) {
		/* sock */
		g_CUdp.GlbNetLimit(GLB_SOCK_MAX);
		GLB_PRINT("Will CSocket.GlbNetInitRecv ...\n");
		if (GLB_SUCCESS == g_CUdp.GlbNetInitRecv(NULL, szPort)) {
			/* thread */
			pCThread = new GlbCls::CGlbThread[iThread];
			GLB_PRINT("Will CThread.GlbThreadCreate ...\n");
			for (iCount = 0; iCount < iThread; iCount ++) {
				(pCThread + iCount)->GlbThreadAttrInit();
				(pCThread + iCount)->GlbThreadRoutineSet(GlbRtbEngThread);
				(pCThread + iCount)->GlbThreadCreate(NULL);
			}
			/* admin */
			do {
				//GlbRtbEngAdmin();
			} while (GLB_SUCCESS == sleep(atoi(g_stConf.m_szSlp)));

			g_CUdp.GlbNetClose();
		}
		else {
			GLB_ERROR("Failed to GlbNetInitRecv\n");
		}

		GlbRtbUtilRwLockDel();
	}
	else {
		GLB_ERROR("Failed to GlbRtbUtilRwLockNew\n");
	}

	return GLB_SUCCESS;
}

void GlbRtbEngAdmin()
{
	GlbNet::CGlbTcp CTcp;
	char szSend[GLB_KBYTES8] = {0};
	char szRecv[GLB_KBYTES8] = {0};

	static char s_szHttp[] =
		"GET / HTTP/1.1\r\n"\
		"Host: %s\r\n"\
		"Connection: keep-alive\r\n"\
		"User-Agent: Mozilla/5.0 (X11; Linux i686; rv:10.0.12) Gecko/20130109 Firefox/10.0.12\r\n"\
		"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"\
		"Accept-Language: en-us,en;q=0.5\r\n"\
		"Accept-Encoding: gzip, deflate\r\n"\
		"\r\n";

	if (GLB_SUCCESS == CTcp.GlbTcpConnect(g_stConf.m_szRest)) {
		CTcp.GlbNetSetTimeOut(GLB_SEC, 0);
		sprintf(szSend, s_szHttp, g_stConf.m_szRest);
		if (CTcp.GlbNetSend((UCHAR*)szSend, strlen(szSend)) > 0) {
			if (CTcp.GlbNetRecv((UCHAR*)szRecv, GLB_KBYTES8) > 0) {
				GlbRtbUtilAdmin(szRecv);
			}
			else {
				#ifdef _GLB_DEBUG_
					GLB_ERROR("Failed to GlbNetRecv\n");
				#endif
			}
		}
		else {
			#ifdef _GLB_DEBUG_
				GLB_ERROR("Failed to GlbNetSend\n");
			#endif
		}
		CTcp.GlbNetClose();
	}
	else {
		#ifdef _GLB_DEBUG_
			GLB_ERROR("Failed to GlbTcpConnect\n");
		#endif
	}

	return;
}

void* GlbRtbEngThread(void* pNULL)
{
	int iLength = 0;
	GLBRTBREQ_S stReq;
	GLBRTBRES_S stRes;
	GlbNet::CGlbPro CPro;
	GlbNet::CGlbUdp CUdp;
	USHORT usTotal = 0;
	int iReturn = GLB_SUCCESS;
	UCHAR uszSend[GLB_KBYTES8] = { 0 };
	UCHAR uszRecv[GLB_KBYTES8] = { 0 };
	GlbNet::GLBPROKV_S stKv[GLB_RN_PAIRS] = {{'\0', 0, NULL}};

	#ifdef _GLB_DEBUG_
		GlbCls::CGlbTime CTime;
	#endif

	CUdp.GlbNetSetSocket(g_CUdp.GlbNetGetSocket());
	while ((iLength = CUdp.GlbNetRecv(uszRecv, GLB_KBYTES8)) > 0) {
		#ifdef _GLB_DEBUG_
			GLB_PRINT("\n");
			CTime.GlbTimeOn();
			GLB_DEBUG("GlbRtbEngThread[%lu] start\n", pthread_self());
		#endif

		/* recv */
		#ifdef _GLB_DEBUG_
			GLB_DEBUG("Recv GlbRtbAdapter %d ...\n", iLength);
			GlbPacketDisplay(uszRecv, iLength);
		#endif

		/* reset */
		usTotal = 0;
		CPro.GlbProReset();
		iReturn = GLB_FAILURE;
		memset(&stReq, '\0', sizeof(GLBRTBREQ_S));
		memset(&stRes, '\0', sizeof(GLBRTBRES_S));

		/* head */
		CPro.GlbProParse(uszRecv, iLength);
		#ifdef _GLB_DEBUG_
			CPro.GlbProDisplay();
		#endif

		/* body */
		GlbProKvInput(stKv, CPro.GlbProGetTotal(), CPro.GlbProGetBody(), CPro.GlbProGetLength());
		#ifdef _GLB_DEBUG_
			GlbProKvDisplay(stKv, CPro.GlbProGetTotal());
		#endif

		/* request */
		GlbRtbUtilReqInput(&stReq, stKv, CPro.GlbProGetLength());
		#ifdef _GLB_DEBUG_
			GLB_PRINT("GlbRtbRequest {\n");
			GlbRtbUtilReqPrint(&stReq);
			GLB_PRINT("}\n");
		#endif

		/* real time bidding */
		#ifdef _GLB_DEBUG_
			GLB_DEBUG("Will GlbRtbEngRtbCore ...\n");
		#endif
		if (GLB_SUCCESS == g_CRwLock.GlbLockTryLock(GLB_RW_RDLK)) {
			iReturn = GlbRtbEngRtbCore(&stReq, &stRes);
			g_CRwLock.GlbLockUnlock();
		}

		/* response */
		if (GLB_SUCCESS == iReturn) {
			GlbRtbUtilResInput(&stRes, stKv, &usTotal);
			#ifdef _GLB_DEBUG_
				GLB_PRINT("GlbRtbResponse {\n");
				GlbRtbUtilResPrint(&stRes);
				GLB_PRINT("}\n");
			#endif
		}
		else {
			CPro.GlbProPutResult(GLB_DEF_UNKN);
		}

		/* body */
		if (usTotal > 0) {
			CPro.GlbProPutLength(GlbProKvOutput(stKv, usTotal, uszSend + GLB_PRO_HEAD, GLB_KBYTES8));
			#ifdef _GLB_DEBUG_
				GlbProKvDisplay(stKv, usTotal);
			#endif
		}
		else {
			CPro.GlbProPutLength(0);
		}

		/* head */
		CPro.GlbProPutBody(NULL);
		CPro.GlbProPutTotal(usTotal);
		CPro.GlbProPutVerb(GLB_RD_RES);
		CPro.GlbProCreate(uszSend, GLB_PRO_HEAD);
		#ifdef _GLB_DEBUG_
			CPro.GlbProDisplay();
		#endif

		/* send */
		#ifdef _GLB_DEBUG_
			GlbPacketDisplay(uszSend, GLB_PRO_HEAD + CPro.GlbProGetLength());
		#endif
		if (CUdp.GlbNetSend(uszSend, GLB_PRO_HEAD + CPro.GlbProGetLength()) > 0) {
			#ifdef _GLB_DEBUG_
				GLB_DEBUG("Send GlbRtbAdapter %d ...\n", GLB_PRO_HEAD + CPro.GlbProGetLength());
			#endif
		}
		else {
			#ifdef _GLB_DEBUG_
				GLB_DEBUG("Send GlbRtbAdapter 0 ...\n");
			#endif
		}

		#ifdef _GLB_DEBUG_
			GLB_DEBUG("GlbRtbEngThread[%lu] over Use time ", pthread_self());
			CTime.GlbTimeOff();
			CTime.GlbTimeDisplay();
		#endif
	}

	pthread_exit(NULL);
}

int GlbRtbEngRtbCore(GLBRTBREQ_S* pstReq, GLBRTBRES_S* pstRes)
{
	GlbCls::CGlbSet CAdvs;
	GLBRTBRESULT_IT CIte;
	GLBRTBRESULT_C CResult;
	int iReturn = GLB_SUCCESS;

	if (GLB_SUCCESS == CAdvs.GlbSetInit(GLB_RD_ASET, sizeof(GLBRAINFO_S**))) {
		CAdvs.GlbSetCompare(GlbRtbEngCompPrice);

		g_CArea.GlbRtbAreaGet(pstReq->m_szUIP, pstReq->m_szAre);
		#ifdef _GLB_DEBUG_
			GLB_DEBUG("%s -> %s\n", pstReq->m_szUIP, pstReq->m_szAre);
		#endif

		GlbRtbEngDimFilter(CResult, pstReq);
		if (true != CResult.GlbMapEmpty()) {
			for (CIte = CResult.GlbMapBegin(); CIte != CResult.GlbMapEnd(); CIte ++) {
				if (GLB_RD_FTAL == CIte->second) {
					CAdvs.GlbSetIndexPut(CAdvs.GlbSetGetCount(), (void**)&CIte->first);
				}
			}
			#ifdef _GLB_DEBUG_
				GLB_DEBUG("GlbRtbEngSetDisplay: ");
				GlbRtbEngSetDisplay(CAdvs);
				GLB_PRINT("\n");
			#endif
		}

		if (CAdvs.GlbSetGetCount() > 0) {
			CAdvs.GlbSetSort();
			GlbRtbEngResValid(pstReq, pstRes, *(GLBRAINFO_S**)CAdvs.GlbSetIndexGet(0));
			#ifdef _GLB_DEBUG_
				GLB_DEBUG("Result -> ID: %s, Price: %s\n", pstRes->m_szAID, pstRes->m_szMcp);
			#endif
		}
		else {
			#ifdef _GLB_DEBUG_
				GLB_DEBUG("Result -> NULL\n");
			#endif
			iReturn = GLB_FAILURE;
		}

		CAdvs.GlbSetDestroy();
	}
	else {
		iReturn = GLB_FAILURE;
	}

	return iReturn;
}

int GlbRtbEngCompPrice(const void* pFirst, const void* pSecond)
{
	return atoi(((GLBRAINFO_S*)pFirst)->m_szPric) - atoi(((GLBRAINFO_S*)pSecond)->m_szPric);
}

void GlbRtbEngTimFilter(GLBRTBRESULT_C& rCResult, GLBRTBREQ_S* pstReq)
{
	ULONG ulCount = 0;
	GLBRAINFO_S* pstInfo = NULL;
	USHORT usTotal = GLB_RD_FTTM;
	char szTim[GLB_DAYTIME] = {0};
	GlbCls::CGlbSet CSet = g_CAdvs.GlbRtbBaseGetRef();

	GlbLocalDayTime(szTim);
	for (; ulCount < CSet.GlbSetGetCount(); ulCount ++) {
		pstInfo = (GLBRAINFO_S*)CSet.GlbSetIndexGet(ulCount);
		if (strcmp(pstInfo->m_szBgTm, szTim) < 0 && strcmp(szTim, pstInfo->m_szEdTm) < 0) {
			rCResult.GlbMapUpdate((ULONG)pstInfo, usTotal);
		}
	}

	return;
}

void GlbRtbEngDimFilter(GLBRTBRESULT_C& rCResult, GLBRTBREQ_S* pstReq)
{
	/* 1.MatchTim */
	GlbRtbEngTimFilter(rCResult, pstReq);
	#ifdef _GLB_DEBUG_
		GLB_PRINT("DimTim: ");
		GlbRtbEngDimDisplay(rCResult);
		GLB_PRINT("\n");
	#endif

	/* 2.MatchAre */
	GlbRtbEngAddResult(g_CLoc, rCResult, pstReq->m_szAre, GLB_RD_FTIP);
	#ifdef _GLB_DEBUG_
		GLB_PRINT("DimAre: ");
		GlbRtbEngDimDisplay(rCResult);
		GLB_PRINT("\n");
	#endif

	/* 3.MatchBrw */
	/*GlbRtbEngAddResult(g_CBrw, rCResult, pstReq->m_szUag, GLB_RD_FTUA);
	#ifdef _GLB_DEBUG_
		GLB_PRINT("DimBrw: ");
		GlbRtbEngDimDisplay(rCResult);
		GLB_PRINT("\n");
	#endif*/

	/* 4.MatchOsn */
	/*GlbRtbEngAddResult(g_COsn, rCResult, pstReq->m_szUag, GLB_RD_FTOS);
	#ifdef _GLB_DEBUG_
		GLB_PRINT("DimOsn: ");
		GlbRtbEngDimDisplay(rCResult);
		GLB_PRINT("\n");
	#endif*/

	/* 5.MatchMid */
	/*GlbRtbEngAddResult(g_CMdi, rCResult, pstReq->m_szUrl, GLB_RD_FTMD);
	#ifdef _GLB_DEBUG_
		GLB_PRINT("DimMid: ");
		GlbRtbEngDimDisplay(rCResult);
		GLB_PRINT("\n");
	#endif*/

	/* 6.MatchTyp */
	GlbRtbEngAddResult(g_CTyp, rCResult, pstReq->m_szSiz, GLB_RD_FTTY);
	#ifdef _GLB_DEBUG_
		GLB_PRINT("DimTyp: ");
		GlbRtbEngDimDisplay(rCResult);
		GLB_PRINT("\n");
	#endif

	return;
}

int GlbRtbEngAddResult(GLBRTBINDEX_C& rCSrc, GLBRTBRESULT_C& rCDst, char* pszKey, USHORT usDim)
{
	UINT uiVal = 0;
	GLBSTRING_C CKey;
	USHORT usTotal = 0;
	int iReturn = GLB_FAILURE;
	GLBRAINFO_S** ppstVal = NULL;

	/* key */
	CKey.append(pszKey);
	if (NULL != (ppstVal = (GLBRAINFO_S**)rCSrc.GlbMapSelect(CKey))) {
		/* val */
		for (uiVal = 0; uiVal < GLB_RA_SUM; uiVal ++) {
			if (NULL != *(ppstVal + uiVal)) {
				usTotal = 0;
				rCDst.GlbMapSelect((ULONG)*(ppstVal + uiVal), usTotal, sizeof(USHORT));
				rCDst.GlbMapUpdate((ULONG)*(ppstVal + uiVal), (usTotal |= usDim));
				iReturn = GLB_SUCCESS;
			}
			else {
				break;
			}
		}
	}

	return iReturn;
}

void GlbRtbEngDimDisplay(GLBRTBRESULT_C& rCResult)
{
	UINT uiCount = 0;
	GLBRTBRESULT_IT CIte;
	USHORT usDim[] = {GLB_RD_FTTM, GLB_RD_FTIP, GLB_RD_FTUA, GLB_RD_FTOS, GLB_RD_FTMD, GLB_RD_FTTY};

	CIte = rCResult.GlbMapBegin();
	while (CIte != rCResult.GlbMapEnd()) {
		GLB_PRINT("%s[", ((GLBRAINFO_S*)(CIte->first))->m_szAdId);
		for (uiCount = 0; uiCount < sizeof(usDim) / sizeof(USHORT); uiCount ++) {
			switch (usDim[uiCount] & CIte->second) {
				case GLB_RD_FTTM:
					GLB_PRINT("T");
					break;
				case GLB_RD_FTIP:
					GLB_PRINT("I");
					break;
				case GLB_RD_FTUA:
					GLB_PRINT("U");
					break;
				case GLB_RD_FTOS:
					GLB_PRINT("O");
					break;
				case GLB_RD_FTMD:
					GLB_PRINT("M");
					break;
				case GLB_RD_FTTY:
					GLB_PRINT("S");
					break;
				default:
					break;
			}
		}
		GLB_PRINT("], ");
		CIte ++;
	}

	return;
}

void GlbRtbEngSetDisplay(GlbCls::CGlbSet rCSet)
{
	UINT uiCount = 0;

	for (; uiCount < rCSet.GlbSetGetCount(); uiCount ++) {
		GLB_PRINT("%s, ", (*(GLBRAINFO_S**)rCSet.GlbSetIndexGet(uiCount))->m_szAdId);
	}

	return;
}

void GlbRtbEngResValid(GLBRTBREQ_S* pstReq, GLBRTBRES_S* pstRes, GLBRAINFO_S* pstInfo)
{
	strcpy(pstRes->m_szBID, pstReq->m_szBID);
	strcpy(pstRes->m_szUIP, pstReq->m_szUIP);
	strcpy(pstRes->m_szPID, pstReq->m_szPID);
	strcpy(pstRes->m_szSiz, pstReq->m_szSiz);
	strcpy(pstRes->m_szAID, pstInfo->m_szAdId);
	strcpy(pstRes->m_szMcp, pstInfo->m_szPric);

	return;
}
